package com.menghao.rpc.provider.handle.tcp;

import com.menghao.rpc.consumer.model.RpcRequest;
import com.menghao.rpc.NamedThreadFactory;
import com.menghao.rpc.exception.InvokeException;
import com.menghao.rpc.provider.model.ProviderKey;
import com.menghao.rpc.provider.regisiter.ProviderRepository;
import com.menghao.rpc.spring.BeansManager;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.concurrent.*;

/**
 * <p>Rpc调用的执行器.</br>
 * <p>线程池执行</p>
 *
 * @author MarvelCode
 */
public class ExecutionExecutor {

    private ThreadPoolExecutor threadPoolExecutor;
    private int queueLimit;
    private static final String THREAD_PREFIX = "Executor-Execution-Task";

    public ExecutionExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, int queueLimit) {
        this.queueLimit = queueLimit;
        threadPoolExecutor = new ExecutionTaskThreadPoolExecutor(corePoolSize, maxPoolSize,
                keepAliveTime, TimeUnit.SECONDS, new ExecutionTaskBlockQueue(),
                new NamedThreadFactory(THREAD_PREFIX, true));
    }


    public void execute(ExecutionTask task) {
        int queueSize = threadPoolExecutor.getQueue().size();
        // TODO 队列溢出处理
        if (queueSize > queueLimit) {

        }
        threadPoolExecutor.execute(task);
    }

    public static class ExecutionTask implements Runnable {

        private ExecutionContext executionContext;

        private ProviderRepository providerRepository;

        ExecutionTask(ExecutionContext executionContext) {
            this.executionContext = executionContext;
            this.providerRepository = BeansManager.getInstance().getBeanByType(ProviderRepository.class);
        }

        @Override
        public void run() {
            RpcRequest rpcRequest = executionContext.getRequest();
            ProviderKey providerKey = new ProviderKey(rpcRequest.getContract(), rpcRequest.getImplCode());
            Object instance = providerRepository.getProvider(providerKey);
            if (instance == null) {
                // 无对应的服务单例，抛异常
                executionContext.writeException(new InvokeException(
                        MessageFormat.format("service {0} not found", providerKey)));
                return;
            }
            try {
                Object result = invoke(instance, rpcRequest);
                executionContext.writeResult(result);
            } catch (Exception e) {
                executionContext.writeException(new InvokeException(e));
            }
        }

        private Object invoke(Object instance, RpcRequest rpcRequest) {
            Method method = ReflectionUtils.findMethod(instance.getClass(), rpcRequest.getMethod(), rpcRequest.getArgsType());
            return ReflectionUtils.invokeMethod(method, instance, rpcRequest.getArgs());
        }
    }

    private class ExecutionTaskThreadPoolExecutor extends ThreadPoolExecutor {


        ExecutionTaskThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        }

        /**
         * TODO 执行前置操作（可扩展，计数同一时刻某方法/某服务并发量）
         */
        @Override
        protected void beforeExecute(Thread t, Runnable r) {
            super.beforeExecute(t, r);
        }

        /**
         * TODO 执行后置操作（可扩展，计数同一时刻某方法/某服务并发量）
         */
        @Override
        protected void afterExecute(Runnable r, Throwable t) {
            super.afterExecute(r, t);
        }
    }

    /**
     * TODO 自定义任务队列
     */
    private class ExecutionTaskBlockQueue extends LinkedBlockingQueue<Runnable> {

    }
}
